##########################################################################################
#  Copyright (c) by Medtronic, Inc., 2013.  
#  $HeadURL: http://build-tool.googlecode.com/svn/trunk/BuildTool/hex2rtl.pl $
#  $Revision: 71 $
#  $Date: 2013-08-12 13:43:22 +0000 (Mon, 12 Aug 2013) $
##########################################################################################


use strict;
use warnings;

use Env qw(BUILD_TOOLS_PATH);
use lib $BUILD_TOOLS_PATH;

use hextools qw(Init_Log Log Get_Timestamp Parse_Intel_Hex_File Calculate_ECC32);

##########################################################################################
#
#    This script takes as input an Intel hex file, strips off all of the Intel formatting,
#    and produces two files: one that contains the bare code content of the Intel hex file,
#    one 32-bit word per line; and another that contains the corresponding ECC values for
#    the code content, one 7-bit ECC value per line.
#
#    Arguments:
#
#            -a <start_addr>:    The starting address for the memory space. This tool
#                                assumes that the code starts at the beginning of the
#                                memory space.
#
#            -i <filename>:      The name of the input intel.hex format file. Default is
#                                code.intel.hex.
#
#            -l <filename>:      The name of the log file. If this argument is omitted,
#                                no log file will be generated.
#
#            -o <filename>:      The name of the RTL output file. The RTL ECC file name
#                                will be derived from this name.
#
#            -s <mem_size>:      The size (number of bytes), in hex, of the memory space.
#                                This allows the script to generate values for the entire
#                                space rather than just the part that contains code.
#
#    Examples:
#
#        This example is for the application code in flash on the Galaxy platform:
#
#            hex2rtl -a 0x10000000 -s 0x80000 -i main_code.intel.hex -o main_code_RTL.hex
#
#        This example is for the trim table in the UserConfig flash sector on the Galaxy
#        platform:
#
#            hex2rtl -a 0x10084010 -s 0x3F0 -i trim.intel.hex -o trim_RTL.hex
#
#        This example is for the flex table in the UserConfig flash sector on the Galaxy
#        platform:
#
#            hex2rtl -a 0x10084400 -s 0x400 -i flexintel.hex -o flex_RTL.hex
#
#  DETAILED DESCRIPTION
#
#    This script parses an Intel hex file, creating an array (indexed by code address)
#    containing the code bytes with all of the Intel formatting data stripped out. It
#    then uses the array to generate two output files for loading the code image in the
#    RTL simulation environment: one for the code (with one 32-bit word per line) and
#    one for the corresponding ECC values (with one 7-bit ECC value per line). These
#    files are used in the RTL simulation environment to avoid having to go through
#    the actual flash programming process.
#
#    The script assumes that there are no gaps (unused addresses) in the used code space.
#    Any gap in the used code area will be flagged as an error (by the Generate_Hex_File
#    subroutine). Unused addresses after the end of the used code area are acceptable.
#
##########################################################################################


#--------------------------------------------------------------------------------------
#
# Initialize constants and global variables.
#
#--------------------------------------------------------------------------------------

my $start_address;
my $memory_size;
my $hex_file_name;
my $rtl_file_name;
my $ecc_file_name;
my $log_file_name;
my $infile;  # File handle

# The memory array holds the code bytes that are extracted from the Intel hex file.
# This array is used for the RTL files. To keep the size of the array to a manageable
# size, it's necessary to subtract the starting address of the code (e.g., 0x01000000
# for ROM or 0x10000000 for flash) when calculating the array index.
my @memory_array;
my $end_address;


#--------------------------------------------------------------------------------------
#
# Process command line arguments
#
#--------------------------------------------------------------------------------------
while (@ARGV and $ARGV[0] =~ /^-/)
{
   $_ = shift;
   if (/^-a/)
   {
      $start_address = hex(shift);
      if (($start_address % 4) != 0) {
          print "Start address must fall on a word boundary\n";
          exit;
      }
   }
   elsif (/^-i/)
   {
      $hex_file_name = shift;
   }
   elsif (/^-l/)
   {
      $log_file_name = shift;
   }
   elsif (/^-o/)
   {
      $rtl_file_name = shift;
   }
   elsif (/^-s/)
   {
      $memory_size = hex(shift);
      if (($memory_size % 4) != 0) {
          print "Memory size must be a multiple of 4\n";
          exit;
      }
   }
   else
   {
      print "Unrecognized switch: $_\n";
      exit;
   }
}

# Check for extraneous arguments
if (defined($ARGV[0]))
{
   print "Invalid argument(s): @ARGV\n";
   exit;
}

# Check for missing arguments
die "No start address provided\n"    unless $start_address;
die "No memory size provided\n"      unless $memory_size;
die "No input file name provided\n"  unless $hex_file_name;
die "No output file name provided\n" unless $rtl_file_name;


#--------------------------------------------------------------------------------------
# 
# Initialize input and output files. Write header information to the log file.
#
#--------------------------------------------------------------------------------------

# Initialize the log file (if the file name is null, no log file will be generated).
Init_Log($log_file_name);

# Open the input hex file. A lexical filehandle is used so that it can be passed as
# an argument to the hextools subroutines.
open($infile,  '<', $hex_file_name)  or die "Can't open $hex_file_name: $!\n";

# Open the RTL output file.
open(RTLOUTFILE, '>', $rtl_file_name) or die "Can't open $rtl_file_name: $!\n";

# Open the ECC output file.
$ecc_file_name = $rtl_file_name;
$ecc_file_name =~ s/\.hex/_ecc.hex/i;
open(RTLECCOUTFILE, '>', $ecc_file_name) or die "Can't open $ecc_file_name: $!\n";

my $timestamp = Get_Timestamp();

my $svn_revision_string = '\$Revision: 71 $';  # Get file revision via SVN keyword
my $revision = "";
if ($svn_revision_string =~ /Revision:\s+(\d+)/)  
{
   $revision = "Revision $1";
}

Log(sprintf "hex2rtl:          $revision\n");
Log(sprintf "Run Time:         $timestamp\n");
Log(sprintf "Input hex file:   $hex_file_name\n");
Log(sprintf "RTL output file:  $rtl_file_name\n");
Log(sprintf "ECC output file:  $ecc_file_name\n");
Log(sprintf "Memory size:      0x%05X\n", $memory_size);
Log(sprintf "Start address:    0x%08X\n", $start_address);


#--------------------------------------------------------------------------------------
# 
# Process the input file. The data from the file will be placed in an array, indexed
# by address.
#
#--------------------------------------------------------------------------------------
$end_address = Parse_Intel_Hex_File($infile, \@memory_array, $start_address, $memory_size);
Log(sprintf "Max Code Address: 0x%05X\n", $end_address);

# Pad the FW image out, if necessary, so it ends on a word boundary.
my $pad_count = ($end_address + 1) % 4;
for (my $i=0; $i < $pad_count; $i++) {
   $end_address++;
   $memory_array[$end_address - $start_address] = sprintf "%02X", 0;
}


#--------------------------------------------------------------------------------------
# 
# Generate the RTL output files (one for the code, one for the corresponding ECC values).
# The format of the output file consists of the offset into a specific section of flash
# and the value (word for data, byte for ecc). The specific section is one of ICP, 
# UserConf, or remaining sectors. All of flash is mapped at 10000000h. ICP offset is
# 80000h and UserConf is 84000h. Since some of the images passed to this tool may not
# start at the beginning of one of these 3 sections, the offset into the section can
# be determined by masking the passed in starting address with 3FFFh to get it's offset.
# ex:
#    app        image 10000000, offset into that section (sectors0-9) 10000000h & 3FFFh = 0h
#    icp patch  image 10080000, offset into that section (ICP)        10080000h & 3FFFh = 0h
#    trim table image 10084000, offset into that section (UserConf)   10084000h & 3FFFh = 0h
#    flex table image 10084800, offset into that section (UserConf)   10084800h & 3FFFh = 800h
#    user patch image 10085000, offset into that section (UserConf)   10085000h & 3FFFh = 1000h
#
#--------------------------------------------------------------------------------------

# Generate the output for the used code space.
printf RTLOUTFILE "\$RADIX   HEX\n";
printf RTLOUTFILE "\$ADDRESS   0    1FFFF\n";
printf RTLECCOUTFILE "\$RADIX   HEX\n";
printf RTLECCOUTFILE "\$ADDRESS   0    1FFFF\n";
my $section_offset = $start_address & 0x3FFF;
my $last = 0;
for (my $index = 0; $index < $end_address - $start_address + 1; $index += 4)
{
   # Code file
   my $wordstr = "$memory_array[$index+3]$memory_array[$index+2]$memory_array[$index+1]$memory_array[$index]";
   printf RTLOUTFILE "%-8X %s\n", ($section_offset + $index) / 4, $wordstr;

   # ECC file
   printf RTLECCOUTFILE "%-8X %02X\n", ($section_offset + $index) / 4, Calculate_ECC32(hex $wordstr);
   $last = $index / 4;
}

# Generate the ecc for the unused memory space (7Fh is the ecc for erased flash).
for (my $index = $end_address - $start_address + 1; $index < $memory_size; $index += 4)
{
   # ECC file
   printf RTLECCOUTFILE "%-8X 7F\n", ($section_offset + $index) / 4;
   $last = $index / 4;
}
printf RTLOUTFILE "\$END\n";
printf RTLECCOUTFILE "\$END\n";

#--------------------------------------------------------------------------------------
# 
# Close the input and output files.
#
#--------------------------------------------------------------------------------------
close($infile);
close(RTLOUTFILE);
close(RTLECCOUTFILE);
